Некритические сбои юнит-теста - PullRequest
5 голосов
/ 10 сентября 2009

Я использую встроенный в Python модуль unittest и хочу написать несколько тестов, которые не являются критическими.

Я имею в виду, если моя программа проходит такие тесты, это здорово! Однако, если это не пройдет, это на самом деле не проблема, программа все равно будет работать.

Например, моя программа предназначена для работы с пользовательским типом «А». Если он не работает с «А», значит, он сломан. Однако для удобства большинство из них также должно работать с другим типом «B», но это не обязательно. Если он не работает с «B», значит, он не сломан (потому что он по-прежнему работает с «A», что является его основным назначением). Отказ от работы с «B» не является критичным, я просто пропущу «бонусную функцию», которую я мог иметь.

Другой (гипотетический) пример - при написании OCR. Алгоритм должен распознавать большинство изображений из тестов, но это нормально, если некоторые из них не удается. (и нет, я не пишу OCR)

Есть ли способ написать некритические тесты в unittest (или другой среде тестирования)?

Ответы [ 10 ]

8 голосов
/ 10 сентября 2009

С практической точки зрения, я бы, вероятно, использовал операторы print для обозначения сбоя в этом случае. Более правильным решением является использование предупреждений:

http://docs.python.org/library/warnings.html

Тем не менее, вы можете использовать средство ведения журнала для создания более подробной записи результатов вашего теста (т. Е. Установить ошибки класса «B» для записи предупреждений в журналы).

http://docs.python.org/library/logging.html

Edit:

То, как мы справляемся с этим в Django, заключается в том, что у нас есть некоторые тесты, которые мы ожидаем провалить, и у нас есть другие, которые мы пропускаем в зависимости от среды. Так как мы обычно можем предсказать, будет ли тест ДОЛЖЕН завершиться неудачей или пройден (то есть, если мы не можем импортировать определенный модуль, система не имеет его, и поэтому тест не будет работать), мы можем интеллектуально пропустить неудачные тесты. Это означает, что мы по-прежнему выполняем каждый пройденный тест и не имеем тестов, которые «могут» пройти. Модульные тесты наиболее полезны, когда они делают вещи предсказуемо и способны определить, должен ли тест пройти, прежде чем мы его запустим, сделать это возможным.

4 голосов
/ 10 сентября 2009

Утверждения в модульных тестах являются бинарными: они будут работать, или они потерпят неудачу, в середине срока нет.

Учитывая, что для создания этих «некритических» тестов вы не должны использовать утверждения, если вы не хотите, чтобы тесты проваливались. Вы должны делать это осторожно, чтобы не поставить под угрозу «полезность» теста.

Мой совет для вашего примера OCR заключается в том, что вы используете что-то для записи показателя успешности в коде тестов, а затем создаете одно утверждение типа: "assert success_rate> 8.5", и это должно дать желаемый эффект.

3 голосов
/ 11 сентября 2009

С unittest документация, на которую вы ссылаетесь:

Вместо unittest.main (), есть другие способы запуска тестов с более тонкий уровень контроля, менее краткий вывод, и не требуется запуск из командной строки. Например, последние две строки могут быть заменены с:

suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)

В вашем случае вы можете создать отдельные TestSuite экземпляры для критических и некритических тестов. Вы можете контролировать, какой набор передается исполнителю теста с помощью аргумента командной строки. Наборы тестов также могут содержать другие наборы тестов, поэтому вы можете создавать большие иерархии, если хотите.

3 голосов
/ 10 сентября 2009

Я не совсем уверен, как работает unittest, но в большинстве платформ модульного тестирования есть что-то похожее на категории. Я полагаю, что вы можете просто классифицировать такие тесты, пометить их как игнорируемые, а затем запускать их только тогда, когда они вам интересны. Но я знаю по своему опыту, что игнорируемые тесты очень быстро становятся ... просто игнорируемыми тестами, которые никто никогда не запускает, и поэтому они являются пустой тратой времени и энергии для их написания.

Мой совет для вашего приложения: делать или нет, нет попытки.

2 голосов
/ 22 октября 2010

В Python 2.7 (и 3.1) добавлена ​​поддержка пропуска некоторых методов тестирования или тестовых случаев, а также пометки некоторых тестов как ожидаемый сбой .

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

Тесты, помеченные как ожидаемые, не будут считаться сбоями в TestResult.

2 голосов
/ 14 сентября 2009

Спасибо за отличные ответы. Не один единственный ответ был действительно полным, поэтому я пишу здесь комбинацию всех ответов, которые помогли мне . Если вам нравится этот ответ, пожалуйста, проголосуйте за людей, которые за это отвечали.

Выводы

Модульные тесты (или, по крайней мере, модульные тесты в модуле unittest) являются двоичными. Как говорит Гильерме Чапьевский, : они сработают или потерпят неудачу, промежуточного срока нет.

Таким образом, я пришел к выводу, что модульные тесты не совсем подходящий инструмент для этой работы. Кажется, что модульные тесты больше озабочены тем, что «все будет работать, никаких сбоев не ожидается» , и поэтому я не могу (или это нелегко) проводить недвоичные тесты.

Итак, модульные тесты не кажутся правильным инструментом, если я пытаюсь улучшить алгоритм или реализацию, потому что модульные тесты не могут сказать мне, насколько лучше одна версия по сравнению с другой (предположим, что обе они правильно выполнены, тогда оба пройдут все юнит-тесты).

Мое окончательное решение

Мое окончательное решение основано на идее Райбера и коде, показанном в wcoenen answer . Я в основном расширяю значение по умолчанию TextTestRunner и делаю его менее многословным. Затем мой основной код вызывает два тестовых набора: критический, использующий стандартный TextTestRunner, и некритический, с моей менее развернутой версией.

class _TerseTextTestResult(unittest._TextTestResult):
    def printErrorList(self, flavour, errors):
        for test, err in errors:
            #self.stream.writeln(self.separator1)
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
            #self.stream.writeln(self.separator2)
            #self.stream.writeln("%s" % err)


class TerseTextTestRunner(unittest.TextTestRunner):
    def _makeResult(self):
        return _TerseTextTestResult(self.stream, self.descriptions, self.verbosity)


if __name__ == '__main__':
    sys.stderr.write("Running non-critical tests:\n")
    non_critical_suite = unittest.TestLoader().loadTestsFromTestCase(TestSomethingNonCritical)
    TerseTextTestRunner(verbosity=1).run(non_critical_suite)

    sys.stderr.write("\n")

    sys.stderr.write("Running CRITICAL tests:\n")
    suite = unittest.TestLoader().loadTestsFromTestCase(TestEverythingImportant)
    unittest.TextTestRunner(verbosity=1).run(suite)

Возможные улучшения

Тем не менее, полезно знать, существует ли какая-либо среда тестирования с недвоичными тестами, например, Кэти Ван Стоун предложила . Возможно, я не буду использовать этот простой личный проект, но он может пригодиться в будущих проектах.

1 голос
/ 10 сентября 2009

Существуют некоторые тестовые системы, которые допускают предупреждения, а не сбои, но test_unit не является одним из них (я не знаю, какие из них делают, необязательно), если вы не хотите расширять его (что возможно).

Вы можете сделать тесты так, чтобы они записывали предупреждения, а не отказывали.

Другой способ справиться с этим - разделить тесты и запускать их только для получения отчетов о прохождении / отказе и не иметь каких-либо зависимостей сборки (это зависит от настроек вашей сборки).

0 голосов
/ 11 сентября 2009

Другая возможность - создать ветку "B" (вы используете какой-то контроль версий, верно?) И иметь там свои модульные тесты для "B". Таким образом, вы сохраняете модульные тесты вашей версии выпуска в чистоте (смотрите, все точки!), Но все еще есть тесты для B. Если вы используете современную систему контроля версий, такую ​​как git или mercurial (я неравнодушен к mercurial), ветвление / клонирование и слияние - тривиальные операции, поэтому я бы порекомендовал.

Тем не менее, я думаю, что вы используете тесты для чего-то, что они не должны делать. Реальный вопрос: «Насколько важно для вас, что« Б »работает?» Потому что в вашем наборе тестов должны быть только те тесты, которые вас волнуют, пройдут они или нет. Проверяет, что, если они терпят неудачу, это означает, что код не работает. Вот почему я предложил тестировать только «B» в ветке «B», так как это будет ветка, в которой вы разрабатываете функцию «B».

При желании вы можете проверить с помощью команд регистратора или команды печати. Но если вас не волнует, что он сломан, чтобы пометить его в ваших модульных тестах, я бы серьезно усомнился, достаточно ли вы для его тестирования. Кроме того, это добавляет ненужную сложность (дополнительные переменные для установки уровня отладки, несколько векторов тестирования, которые полностью независимы друг от друга, но работают в одном и том же пространстве, вызывая потенциальные коллизии и ошибки и т. Д. И т. Д.). Если вы не разрабатываете "Hello, World!" приложение, я подозреваю, что ваш набор проблем достаточно сложен, без добавления дополнительных, ненужных осложнений.

0 голосов
/ 10 сентября 2009

Взгляните на нос: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/

Существует множество параметров командной строки для выбора тестов для запуска, и вы можете сохранить существующие тесты юнит-тестов.

0 голосов
/ 10 сентября 2009

Вы можете написать свой тест, чтобы они посчитали вероятность успеха. С помощью оптического распознавания символов вы можете получить 1000 изображений кода и добиться успеха на 95%.

Если ваша программа должна работать с типом А, то в случае неудачи тест завершается неудачей. Если нет необходимости работать с B, какова ценность проведения такого теста?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...